Letzte Woche haben wir gesagt, dass Vorhersagen der Gehirnaktivität einer Person aus der Aktivität anderer Personen nicht nur schwieriger ist, sondern auch Potential hat.
Was Besonderheiten der Aufgaben angeht, so haben wir zwar bei Neurosynth den Nachteil, dass es (zumindest ohne viel manueller Arbeit), nicht möglich ist nur nach Gesichterstudien zu suchen, bei denen die Gesichter bloß vorgestellt werden sollten. Daher werden unsere Aktivierungen zu denen aus anderen Gesichterstudien nicht allzu gut passen.
Aber wir haben den großen Vorteil, dass wir zur Zeit ca. 3000 verschiedene Stichwörter in Neurosynth haben, von denen viele Synonyme sind (face,faces,facial etc.) oder bedeutungslos (magnetic, gyrus), aber die meisten sind für uns schon ganz interessant. Was für Möglichkeiten eröffnet das? Während wir mit unserer Versuchsperson kaum mehr als ein paar Bedingungen in einer Stunde durchlaufen können, haben wir hier eine Datenbank mit mindestens mehreren hundert Karten für spezifische Prozesse. Das heißt, dass wir versuchen können, Hirnbilder unserer Person zu dekodieren, wo sie an Dinge gedacht hat die nie in unserem Trainingsdatensatz waren. Das heißt, dass wir jemanden in den Scanner legen könnten und sofort jeden nur vorstellbaren Gedanken noch so fein aufschlüsseln könnten, egal ob wir Trainingsdaten haben und egal an was die Person denkt. Das hat aber seine Grenzen, weil sehr spezifische Gedanken nicht unbedingt auf die gleiche Art in unser aller Gehirn repräsentiert sind. Vielleicht verarbeiten wir alle Gesichter in der FFA, aber wir haben nicht alle das gleiche Aktivierungsmuster in der FFA wenn wir ein fröhliches Gesicht sehen etc.
Wir vergleichen heute ein paar hundert Karten aus Neurosynth erst untereinander (um Gruppen von ähnlichen Funktionen zu finden) und dann mit jedem Block unserer Versuchsperson. Das ergibt viele Daten, die wir entsprechend in einer vereinfachten Art und Weise darstellen wollen.
Wir machen uns eine Liste mit allen unseren Hirnbildern¶
import os
imgList = ['../training/%s'%x for x in os.listdir('../training/') if x.startswith('s00')]; imgList.sort()
Whole-Brain Maske¶
Hier benutzen wir eine grobe Maske mit 4mm Auflösung, weil die Berechnungen sonst zu lange dauern
from nilearn import image, plotting, input_data
import pickle
my_masker = input_data.NiftiMasker(mask_img='../masks/gmMap4mm.nii.gz').fit()
my_masker
plotting.plot_roi(my_masker.mask_img_);
Daten extrahieren¶
from sklearn import preprocessing
def extractMaps(fileName,my_masker):
thisData = my_masker.transform(fileName)[-1]
scaleData = preprocessing.scale(thisData)
return scaleData
Um das Ganze erstmal beispielhaft durchzugehen, nehmen wir uns einen einzelnen Block aus unseren Daten
thisMap = imgList[-19]
scaleMap = extractMaps(thisMap,my_masker)
scaleMap
wir laden ca. 600 Karten aus Neurosynth, die ich vorher ausgewählt habe¶
Die Einschlusskriterien waren, dass die Maske nicht leer sein darf (Stichwörter wie "magnetic" sind so unspefifisch, dass sie keine Voxel enthalten; außerdem gibt es sehr viele Karten, die zwar Voxel enthalten, aber keinem inhaltlich interpertierbaren psychologischen Prozess zugeordnet werden können (z.B. white matter, young, old, patient, healthy).
import pandas as pd
nsData = pd.read_csv('../arrays/ns_4mm_database.csv',index_col=[0,1])
nsData
Die Daten wurden mittels Clusteranalyse in 8 verschiedene Cluster eingeteilt, denen wir erstmal keine Namen geben; das können Sie selbst einmal machen, indem Sie sich erstmal die Ergebnisse unten ansehen, dann die Werte hier ändern und dann alles ab hier noch mal ausführen.
clusterNames = {'0':'a',
'1':'b',
'2':'c',
'3':'d',
'4':'e',
'5':'f',
'6':'g',
'7':'h',
}
Korrelation eines Blocks unserer Daten mit allen 602 Karten¶
import numpy as np
thisCorr = np.corrcoef(scaleMap,nsData)[0,:][1:]
thisCorrDf = pd.DataFrame(thisCorr,index=nsData.index,columns=['corr']).T
thisCorrDf
Wir sortieren nach Stärke der Korrelation¶
Sonst müssten wir die 602 Korrelationen einzeln durchgehen um bedeutsame Zusammenhänge zu finden.
def getTop(corrDf):
sortDf = corrDf.copy()
sortDf = sortDf.T.sort_values(by=sortDf.index[-1],ascending=False)
topDf = pd.concat([sortDf[:5],sortDf[-5:]],axis=0)
topDf.columns = ['correlation']
topDf = topDf.round(2)
return topDf
topDf = getTop(thisCorrDf)
Beispiel: Die fünf höchstn und die fünf niedrigsten Korrelationen des gewählten Blocks
topDf
Auflösung: was war unser Block?
thisMap
Die 602 Karten in Neurosynth nach Ähnlichkeit gruppieren¶
Wir haben gesagt, dass die 602 Karten in Cluster eingeteilt sind. Können wir das irgendwie darstellen? Ja, indem wir die 18744 Dimensionen (jeder Voxel ist eine Dimension) auf 2 Dimensionen projizieren. Dazu verwenden wir Multidimensionale Skalierung.
# hier verwenden wir Daten die ich vorbereitet habe
dissDf = pd.read_csv('../arrays/dissDf.csv',index_col=[0])
mdsPositions = np.array(pd.read_csv('../arrays/mdsDf.csv',index_col=[0]))
mdsDf = pd.DataFrame(mdsPositions,index=dissDf.index)
# plotting
import matplotlib.pyplot as plt
from matplotlib import gridspec
import seaborn as sns
%matplotlib inline
Acht Fraben für die acht Cluster:
myPalette = sns.color_palette('Set1',n_colors=8)
sns.palplot(myPalette)
Brain Space¶
# laden von vorbereiteten Sachen
mdsDf = pd.read_csv('../arrays/mdsDf.csv',index_col=[0])
kDf = pd.read_csv('../arrays/kDf.csv',index_col=[0])
Abbildung erstellen
def findNeighbors(mdsDf,p,added,notCloserThan=50):
# coordinates of this keyword
thisDf = mdsDf.loc[p]
# coordinates of all other keywords
otherDf = mdsDf.drop(p)
# lenghts of adjacent and opposite
diffDf = abs(thisDf-otherDf)
# lengths of hypoteneuse
distanceDf = np.sqrt(diffDf**2).sum(axis=1)
# check if there are close distances
closeEncounters = distanceDf[distanceDf<notCloserThan].index
# check if the close ones have already been labelled
for entry in closeEncounters:
if entry in added:
return True
return False
def plotSpace(mdsDf,kDf,clusterNames,myPalette,closest,ax):
kPredictions = list(kDf['n'])
# loop both trough the positions and the predictions
for p,l in zip(mdsDf.index,kPredictions):
# show predictions from raw data on mds scaled data, the predictions are indicated by the color
ax.plot( mdsDf.ix[p]['0'],
mdsDf.ix[p]['1'],
'o',color=myPalette[l],
markersize=12,alpha=0.8
)
added = []
mdsDf = mdsDf.sort_values(by='1')
# sorting by the x-dimension will fill the labels from right to left side
mdsDf = mdsDf.sort_values(by='0',ascending=False)
for p,x,y in zip(mdsDf.index,mdsDf['0'], mdsDf['1']):
l = kDf.loc[p]
if not findNeighbors(mdsDf,p,added,notCloserThan=closest):
ax.annotate(p, xy = (x, y),fontsize=16,alpha=0.8)
added.append(p)
# dummy plot for labelling
for l in np.unique(kDf['n']):
ax.plot([],'o',color=myPalette[l],label=clusterNames[str(l)])
# show the plot
sns.despine(left=True,bottom=True)
ax.set_xticks([]);ax.set_yticks([])
ax.legend(loc='lower left',bbox_to_anchor=(0.,0))
return ax
Einstellungen für Abbildung
sns.set_style('white')
sns.set_context('poster')
der große Ball¶
fig,ax = plt.subplots(1,1,figsize=(16,16))
ax = plotSpace(mdsDf,kDf,clusterNames,myPalette,50,ax)
plt.show()
Was ist das? Jede der 602 Karten in Neurosynth wurde mit jeder anderen korreliert. Das gibt 602x602=362404 Korrelationen.
Wir machen aus den Korrelationen Distanzen, indem wir 1-Korrelation rechnen. Perfekte Korrelationen von 1 haben eine Distanz von 0 und negative Korrelationen von -1 haben eine Distanz von 2. Alle anderen liegen dazwischen. Dann nehmen wir diese Distanzen im 18744-Dimensionalen Raum (jeder Voxel ist eine Dimension) und projizieren sie auf 2. Dabei geht zwar viel Information verloren, aber weniger als man denken würde. Da wir außerdem eine Clusteranalyse im 18744-Dimnesionalen Raum gerechnet haben, können wir jedem Punkt auf der Karte eine Farbe geben, die anzeigt, zu welchem Cluster der Punkt gehört. Dabei sehen wir, dass die Cluster ganz gut in 2D dargestellt werden können.
Was sagt uns das? Je näher sich zwei Punkte sind, desto ähnlicher sind sich die Aktivierungsmuster für das jeweilige Stichwort in Neurosynth. Für sich genommen ist schon interessant, wie gut inhaltlich/psychologisch interpretierbar diese Ähnlichkeiten sind.
der Ball - Teil 2¶
Jetzt haben wir zwar alle Karten in Neurosynth miteinander korreliert, aber unsere eigenen Daten aber gar nicht verwendet um den Ball zu erstellen. Dabei hatten wir doch oben eine Tabelle mit Korrelationen. Um diese jetzt auf dem Ball darzustellen, variieren wir die Größe der Punkte. Je höher die Korrelation unserer Karte mit einem Punkt (z.B. "face", "lexical" etc.), desto größer wird der Punkt.
Dabei schummeln wir, indem wir die Größe exponentiell variieren. D.h. die Unterschiede in unserem Ball sehen viel viel größer aus als sie tatsächlich sind. Das ist aber der einzige Weg, dass das resultierende Bild dann noch halbwegs übersichtlich und lesbar ist. Außerdem simuliert das auch in gewisser Weise einen "winner take all" Algorithmus.
# Skalierung der Korrelationen von 0 bis 1
def makeMinMax(corrDf):
minMaxDf = pd.DataFrame( preprocessing.minmax_scale(corrDf,axis=1),
index=corrDf.index,
columns=corrDf.columns )
return minMaxDf.T
minMaxDf = makeMinMax(thisCorrDf)
Abbildung machen
def plotCorr(mdsDf,kDf,minMaxDf,myPalette,clusterNames,closest,ax):
# loop both trough the positions and the predictions
for p in mdsDf.index:
l = kDf['n'].loc[p]
# show predictions from raw data on mds scaled data, the predictions are indicated by the color
ax.plot( mdsDf.ix[p]['0'],
mdsDf.ix[p]['1'],
'o',color=myPalette[l],
markersize=minMaxDf.ix[l].ix[p]**3*50,
alpha=0.7
)
# to not omit the most important keywords, we move through the list by the order in minMaxDf
sortedIndex = minMaxDf.sort_values('corr',ascending=False).index.labels[1]
sortedNames = [minMaxDf.index.levels[1][x] for x in sortedIndex ]
#print sortedNames[:10]
added = []
for p in sortedNames:
l = kDf['n'].loc[p]
x = mdsDf['0'].loc[p]
y = mdsDf['1'].loc[p]
if not findNeighbors(mdsDf,p,added,notCloserThan=closest):
thisVal = minMaxDf.ix[l].ix[p].values[-1]
ax.annotate(p, xy = (x, y),
fontsize=thisVal**3*50,
alpha=thisVal**5)
added.append(p)
# dummy plot for labelling
for l in np.unique(kDf['n']):
ax.plot([],'o',color=myPalette[l],label=clusterNames[str(l)])
# show the plot
sns.despine(left=True,bottom=True)
ax.set_xticks([]);ax.set_yticks([])
#ax.legend(loc='lower left',bbox_to_anchor=(1.5,0))
return ax
fig,ax = plt.subplots(1,1,figsize=(8,8))
plotCorr(mdsDf,kDf,minMaxDf,myPalette,clusterNames,100,ax)
plt.show()
Sanity Checks¶
def makeMindSpace(thisMap,
my_masker=my_masker,nsData=nsData,mdsDf=mdsDf,kDf=kDf,
myPalette=myPalette,clusterNames=clusterNames):
scaleMap = extractMaps(thisMap,my_masker)
thisCorr = np.corrcoef(scaleMap,nsData)[0,:][1:]
thisCorrDf = pd.DataFrame(thisCorr,index=nsData.index,columns=['corr']).T
minMaxDf = makeMinMax(thisCorrDf)
fig,ax = plt.subplots(1,1,figsize=(8,8))
plotCorr(mdsDf,kDf,minMaxDf,myPalette,clusterNames,50,ax)
imgName = thisMap.split('/')[-1].split('.')[0]
cond,num,content = imgName.split('_')
plt.title('%s %s %s' % (cond,num,content))
plt.show()
makeMindSpace('../ns/autobiographical memory_pFgA_pF=0.50_FDR_0.01.nii.gz')
makeMindSpace('../ns/language comprehension_pFgA_pF=0.50_FDR_0.01.nii.gz')
makeMindSpace('../ns/emotionally_pFgA_pF=0.50_FDR_0.01.nii.gz')
Das machen wir jetzt für alle 40 unserer Trainingsblöcke¶
def sortBlocks(blocks):
d = {}
for i in blocks:
num = i.split('/')[-1].split('.')[0].split('_')[1]
d[int(num)] = i
sortRunDf = pd.DataFrame(d,index=['filename']).T
sortRunDf.sort_index(inplace=True)
return sortRunDf
sortRunDf = sortBlocks(imgList)
sortRunDf
for i in sortRunDf.index:
thisBlock = sortRunDf.ix[i]['filename']
ax = makeMindSpace(thisBlock)